package btree4j;

import btree4j.utils.collections.longs.LongHash;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class BTreeIndexPrinter {

    public static void printIndexStructure(BTreeIndex index, String filename) throws Exception {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) {
            writer.write("========== Index Structure ==========\n");
            printBTreeStructure(index, writer);

            writer.write("\n\n========== Data Storage Structure ==========\n");
            printDataPages(index, writer);
        }
    }

    private static void printBTreeStructure(BTreeIndex index, BufferedWriter writer) throws Exception {
        BTree.BTreeNode root = index.getRootNode();
        Set<Long> visited = new HashSet<>();
        printIndexNode(index, root, writer, 0, visited);
    }

    private static void printIndexNode(BTreeIndex index, BTree.BTreeNode node,
                                       BufferedWriter writer, int level,
                                       Set<Long> visited) throws Exception {
        long pageNum = node.page.getPageNum();
        if (visited.contains(pageNum)) {
            writer.write(getIndent(level) + "Node " + pageNum + " (already visited)\n");
            return;
        }
        visited.add(pageNum);

        String indent = getIndent(level);
        byte status = node.ph.getStatus();
        String type = (status == BTree.LEAF) ? "Leaf" : "Branch";
        int keyCount = node.keys.length;
        int ptrCount = node.ptrs.length;

        writer.write(String.format("%sNode %d (Level %d, %s, Keys: %d, Pointers: %d)\n",
                indent, pageNum, level, type, keyCount, ptrCount));
        // 应添加以下代码以显示双向指针：
        if (status == BTree.LEAF) {
            writer.write(String.format("%s  Prev: %d, Next: %d\n", indent, node.prev, node.next));
        }

        // 修改点1：使用新的键值解码方法
        for (int i = 0; i < keyCount; i++) {
            Value key = node.keys[i];
            String fullKey = decodeKeyWithPrefix(node, key);
            writer.write(String.format("%s  Key[%d]: %s\n", indent, i, fullKey));
        }

        if (status == BTree.BRANCH) {
            for (int i = 0; i < ptrCount; i++) {
                long childPageNum = node.ptrs[i];
                writer.write(String.format("%s  Pointer[%d] -> Node %d\n", indent, i, childPageNum));
                try {
                    BTree.BTreeNode child = index.getChildNode(node, i);
                    if (child != null) {
                        printIndexNode(index, child, writer, level + 1, visited);
                    } else {
                        writer.write(String.format("%s    [Error: Child Node %d not found]\n", indent, childPageNum));
                    }
                } catch (BTreeException e) {
                    writer.write(String.format("%s    [Error accessing Child Node %d: %s]\n", indent, childPageNum, e.getMessage()));
                }
            }
        } else {
            for (int i = 0; i < ptrCount; i++) {
                long ptr = node.ptrs[i];
                long dataPageNum = BTreeIndex.getPageNumFromPointer(ptr);
                int tid = BTreeIndex.getTidFromPointer(ptr);
                writer.write(String.format("%s  Data Pointer[%d]: Page %d, TID %d\n", indent, i, dataPageNum, tid));

                try {
                    BTreeIndex.DataPage dataPage = index.getDataPage(dataPageNum);
                    byte[] data = dataPage.get(tid);
                    if (data == null) {
                        writer.write(String.format("%s    Data: null\n", indent));
                    } else {
                        String preview = data.length > 20 ?
                                new String(Arrays.copyOf(data, 20), StandardCharsets.UTF_8) + "..." :
                                new String(data, StandardCharsets.UTF_8);
                        writer.write(String.format("%s    Data Preview: %s\n", indent, preview));
                    }
                } catch (BTreeException e) {
                    writer.write(String.format("%s    [Error retrieving data: %s]\n", indent, e.getMessage()));
                }
            }
        }
    }

    // 修改点2：添加前缀还原方法
    private static String decodeKeyWithPrefix(BTree.BTreeNode node, Value key) {
        try {
            Field prefixField = BTree.BTreeNode.class.getDeclaredField("prefix");
            prefixField.setAccessible(true);
            Value prefix = (Value) prefixField.get(node);

            byte[] prefixBytes = prefix.getData();
            byte[] keyBytes = key.getData();
            byte[] fullKey = new byte[prefixBytes.length + keyBytes.length];
            System.arraycopy(prefixBytes, 0, fullKey, 0, prefixBytes.length);
            System.arraycopy(keyBytes, 0, fullKey, prefixBytes.length, keyBytes.length);
            return new String(fullKey, StandardCharsets.UTF_8);
        } catch (Exception e) {
            return "[Error decoding key]";
        }
    }

    private static String getIndent(int level) {
        return String.join("", Collections.nCopies(level, "  "));
    }

    private static void printDataPages(BTreeIndex index, BufferedWriter writer) throws Exception {
        writer.write(String.format("%-8s | %-8s | %-6s | %s\n",
                "Page#", "TupleID", "Length", "Data Preview"));
        writer.write("---------+----------+--------+----------------\n");

        for (LongHash.BucketEntry<BTreeIndex.DataPage> entry : index.dataCache) {
            BTreeIndex.DataPage dataPage = entry.getValue();
            long pageNum = dataPage.getPageNum();
            List<byte[]> tuples = dataPage.tuples;

            for (int tid = 0; tid < tuples.size(); tid++) {
                byte[] data = tuples.get(tid);
                String preview = data.length > 20 ?
                        new String(Arrays.copyOf(data, 20), StandardCharsets.UTF_8) + "..." :
                        new String(data, StandardCharsets.UTF_8);
                writer.write(String.format("%-8d | %-8d | %-6d | %s\n",
                        pageNum, tid, data.length, preview));
            }
        }
    }

    public static void main(String[] args) throws Exception {
        File file = new File("index_test.idx");
        BTreeIndex index = new BTreeIndex(file);
        index.init(false);



        for (int i = 0; i < 1000; i++) {
            String key = String.format("key%04d", i);
            String value = "value-" + key;
            index.putValue(new Value(key.getBytes(StandardCharsets.UTF_8)),
                    value.getBytes(StandardCharsets.UTF_8));
        }

        printIndexStructure(index, "index_structure_afterinsert.txt");
        System.out.println("索引结构已保存到 index_structure_afterinsert.txt");

        // 删除测试数据
        for (int i = 120; i < 300; i++) {
            String key = String.format("key%04d", i);
            index.removeValue(new Value(key.getBytes(StandardCharsets.UTF_8)));
        }

        // 打印删除后的树结构
        printIndexStructure(index, "index_structure_afterdelete.txt");
        System.out.println("删除后的索引结构已保存到 index_structure_afterdelete.txt");

        // 关闭索引
        index.flush();
        index.close();
    }
}